package com.ruoyi.backend.controller;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.backend.util.MinioUtil;
import com.ruoyi.backend.vo.FileMergeReqVO;
import com.ruoyi.common.core.domain.R;


import io.minio.MinioClient;
import io.minio.http.Method;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;


@RestController
@RequestMapping("/backend/minio")
@RequiredArgsConstructor
public class MinioController {

    private static final Logger logger = LoggerFactory.getLogger(MinioController.class);

    @Autowired
    private MinioUtil minioUtil;
    @Autowired
    private MinioClient minioClient;
    @Autowired
    private StringRedisTemplate redisTemplate;

    private final ObjectMapper objectMapper = new ObjectMapper(); // Jackson

    /**
     * 创建存储bucket
     *
     * @param bucketName
     * @return
     */
    @PostMapping("makeBucket")
    public R<Boolean> makeBucket(String bucketName) {
        try {
            boolean success = minioUtil.makeBucket(bucketName);
            if (success) {
                return R.ok(true, "存储桶创建成功");
            } else {
                return R.fail("存储桶创建失败");
            }
        } catch (Exception e) {
            return R.fail("创建存储桶时发生错误: " + e.getMessage());
        }
    }

    /**
     * 删除存储bucket
     *
     * @param bucketName
     * @return
     */
    @PostMapping("removeBucket")
    public R<Boolean> removeBucket(String bucketName) {
        try {
            boolean success = minioUtil.removeBucket(bucketName);
            if (success) {
                return R.ok(true, "存储桶删除成功");
            } else {
                return R.fail("存储桶删除失败");
            }
        } catch (Exception e) {
            return R.fail("删除存储桶时发生错误: " + e.getMessage());
        }
    }

    /**
     * 查看存储bucket是否存在
     *
     * @param bucketName
     * @return
     */
    @GetMapping("bucketExists")
    public R<Boolean> bucketExists(String bucketName) {
        try {
            boolean exists = minioUtil.bucketExists(bucketName);
            if (exists) {
                return R.ok(true, "存储桶存在");
            } else {
                return R.ok(false, "存储桶不存在");
            }
        } catch (Exception e) {
            return R.fail("检查存储桶时发生错误: " + e.getMessage());
        }
    }

    /**
     * 文件上传到指定桶
     *
     * @param file       文件
     * @param bucketName 桶名称
     * @return
     */
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<String> uploadFile(
            @RequestParam("file") MultipartFile file,
            @RequestParam("bucketName") String bucketName) {

        try {
            String fileName = file.getOriginalFilename();
            String fileUrl = minioUtil.uploadFile(bucketName, fileName, file);
            // 备份
            minioUtil.uploadToBackup(file);

            return R.ok(fileUrl, "上传成功");
        } catch (Exception e) {
            return R.fail("上传失败: " + e.getMessage());
        }
    }

    /**
     * 获取对象url（从指定桶）
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @return
     */
    @GetMapping("getObjectUrl")
    public R<String> getObjectUrl(
            @RequestParam("bucketName") String bucketName,
            @RequestParam("objectName") String objectName) {

        try {
            String fileUrl = minioUtil.getObjectUrl(bucketName, objectName);
            return R.ok(fileUrl, "获取成功");
        } catch (Exception e) {
            return R.fail("获取文件URL失败: " + e.getMessage());
        }
    }

    /**
     * 删除指定桶中的文件
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @return
     */
    @PostMapping("removeFile")
    public R<Object> removeFile(
            @RequestParam("bucketName") String bucketName,
            @RequestParam("objectName") String objectName) {

        try {
            minioUtil.removeFile(bucketName, objectName);
            return R.ok("删除成功");
        } catch (RuntimeException e) {
            return R.fail(e.getMessage());
        }
    }

//    /**
//     * 上传文件分片
//     *
//     * @param fileId      文件标识符
//     * @param filePart    文件分片
//     * @param chunkIndex  当前分片索引
//     * @param totalChunks 总分片数
//     * @return map
//     */
//    @PostMapping(value = "/uploadPart", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
//    public R<Map<String, Object>> uploadFilePart(@RequestParam("bucketName") String bucketName,
//                                                 @RequestParam("fileId") String fileId,
//                                                 @RequestParam("fileName") String fileName,
//                                                 @RequestParam("filePart") MultipartFile filePart,
//                                                 @RequestParam("chunkIndex") Integer chunkIndex,
//                                                 @RequestParam("totalChunks") Integer totalChunks) {
//        try {
//            // 对文件名做 URL Encode
//            String safeFileName = java.net.URLEncoder.encode(fileName, "UTF-8");
//
//            // 上传分片
//            String objectName = minioUtil.uploadFilePart(bucketName, fileId, safeFileName, filePart, chunkIndex, totalChunks);
//
//            // 使用 Redis Set 保存已上传分片，实现原子性
//            String key = "upload:" + fileId;
//            redisTemplate.opsForSet().add(key, chunkIndex.toString());
//            redisTemplate.expire(key, 1, TimeUnit.DAYS);//设置过期时间
//            // 获取已上传分片数量
//            Long uploadedCount = redisTemplate.opsForSet().size(key);
//
//            Map<String, Object> map = new HashMap<>();
//            map.put("success", true);
//            map.put("objectName", objectName);
//            map.put("uploadedChunks", uploadedCount);
//            map.put("totalChunks", totalChunks);
//            map.put("progress", totalChunks == 0 ? "0%" : (uploadedCount * 100 / totalChunks) + "%");
//
//            return R.ok(map);
//        } catch (Exception e) {
//            e.printStackTrace();
//            return R.fail("分片上传失败: " + e.getMessage());
//        }
//    }

    /**
     * 查看当前已上传分片（查看上传进度）
     *
     * @param fileId
     * @return
     */
    @GetMapping("/checkUploadedChunks")
    public R<List<Integer>> checkUploadedChunks(@RequestParam String fileId) {
        try {
            String key = "upload:" + fileId;
            // 获取 Set 中所有已上传分片
            Set<String> uploadedSet = redisTemplate.opsForSet().members(key);
            List<Integer> uploadedChunks = new ArrayList<>();
            if (uploadedSet != null) {
                for (String idx : uploadedSet) {
                    uploadedChunks.add(Integer.parseInt(idx));
                }
            }
            return R.ok(uploadedChunks);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("获取已上传分片失败: " + e.getMessage());
        }
    }


    /**
     * 秒传检测：判断文件是否已存在
     *
     * @param bucketName 桶名
     * @param fileId     文件唯一标识
     * @param fileName   文件名
     * @return R
     */
    @GetMapping("/checkFileExists")
    public R<Map<String, Object>> checkFileExists(
            @RequestParam String bucketName,
            @RequestParam String fileId,
            @RequestParam String fileName) {
        try {
            // 合并后的对象路径（与 merge 一致）
            String mergedObjectName = "merged/" + fileId + "/" + java.net.URLEncoder.encode(fileName, "UTF-8");

            boolean exists = minioUtil.doesObjectExist(bucketName, mergedObjectName);

            Map<String, Object> result = new HashMap<>();
            result.put("exists", exists);

            if (exists) {
                String fileUrl = minioUtil.getObjectUrl(bucketName, mergedObjectName);
                result.put("fileUrl", fileUrl);
                return R.ok(result, "文件已存在，支持秒传");
            } else {
                return R.ok(result, "文件不存在，需要上传");
            }

        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("秒传检测失败: " + e.getMessage());
        }
    }

    /**
     * 获取文件分片上传的预签名 URL（前端直传）
     */
    @GetMapping("/getPresignedPartUrl")
    public R<Map<String, Object>> getPresignedPartUrl(
            @RequestParam String bucketName,
            @RequestParam String fileId,
            @RequestParam String fileName,
            @RequestParam Integer chunkIndex) {
        try {
            //统一编码
            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
            String objectName = fileId + "/" + encodedFileName + "-" + chunkIndex;

            String presignedUrl = minioClient.getPresignedObjectUrl(
                    io.minio.GetPresignedObjectUrlArgs.builder()
                            .method(Method.PUT)
                            .bucket(bucketName)
                            .object(objectName)
                            .expiry(1, TimeUnit.HOURS)
                            .build()
            );

//            // 将 MinIO 生成的内网预签名URL替换成本地代理地址
//            String publicUrl = presignedUrl
//                    .replace("192.168.102.4:9000", "10.102.6.30:9000");


            Map<String, Object> result = new HashMap<>();
            result.put("url", presignedUrl);
            result.put("objectName", objectName);
            result.put("expireIn", "1 hour");
            logger.info("获取url结果: {}",result);
            return R.ok(result, "获取分片上传URL成功");
        } catch (Exception e) {
            logger.error("获取分片上传URL失败: {}", e.getMessage(), e);
            return R.fail("获取分片上传URL失败: " + e.getMessage());
        }
    }

    /**
     * 合并通过预签名上传的分片文件（MinIO 服务端直接合并）
     */
    @PostMapping("/mergePartsPresigned")
    public R<Map<String, Object>> mergePartsPresigned(@RequestBody FileMergeReqVO reqVO) {
        try {
            String fileUrl = minioUtil.mergeFilePartsByCompose(reqVO);

            Map<String, Object> result = new HashMap<>();
            result.put("mergedFileUrl", fileUrl);
            result.put("success", true);
            logger.info("合并结果: {}",result);
            return R.ok(result, "文件合并成功（预签名直传模式）");
        } catch (Exception e) {
            logger.error("分片合并失败: {}", e.getMessage(), e);
            return R.fail("分片合并失败: " + e.getMessage());
        }
    }

    @GetMapping("/downloadToServer")
    public R<String> downloadToServer(
            @RequestParam String fileName,
            @RequestParam(defaultValue = "satellite") String bucketName) {

        boolean success = minioUtil.downloadToServer(bucketName, fileName);
        return success ? R.ok("文件下载成功") : R.fail("文件下载失败");
    }

    /**
     * 上传文件夹 - 立即返回任务ID，后台异步处理
     */
    @PostMapping("/uploadFolder")
    public R<Map<String, Object>> uploadFolder(
            @RequestParam String bucketName,
            @RequestParam String folderPath) {

        try {
            // 生成任务ID
            String taskId = "upload_" + System.currentTimeMillis() + "_" +
                    UUID.randomUUID().toString().substring(0, 8);

            // 异步执行上传任务
            CompletableFuture.runAsync(() -> {
                try {
                    minioUtil.uploadFolder(bucketName, folderPath, taskId);
                } catch (Exception e) {
                    logger.error("异步上传任务失败: {} -> {}", taskId, e.getMessage(), e);
                }
            });

            // 立即返回任务ID
            Map<String, Object> response = new HashMap<>();
            response.put("taskId", taskId);
            response.put("status", "processing");
            response.put("message", "文件夹上传任务已开始");
            response.put("startTime", new Date().toString());
            response.put("checkStatusUrl", "/backend/minio/uploadStatus?taskId=" + taskId);

            return R.ok(response, "上传任务已启动");

        } catch (Exception e) {
            logger.error("启动上传任务失败: {}", e.getMessage(), e);
            return R.fail("启动上传任务失败: " + e.getMessage());
        }
    }

    /**
     * 查询上传任务状态
     */
    @GetMapping("/uploadStatus")
    public R<Map<String, Object>> getUploadStatus(@RequestParam String taskId) {
        try {
            Map<String, Object> taskStatus = minioUtil.getTaskStatus(taskId);
            return R.ok(taskStatus, "任务状态查询成功");

        } catch (Exception e) {
            logger.error("查询任务状态失败: {}", e.getMessage(), e);
            return R.fail("查询任务状态失败: " + e.getMessage());
        }
    }

    /**
     * 获取所有进行中的上传任务
     */
    @GetMapping("/uploadTasks")
    public R<List<Map<String, Object>>> getUploadTasks() {
        try {

            return R.ok(new ArrayList<>(), "获取任务列表成功");
        } catch (Exception e) {
            logger.error("获取任务列表失败: {}", e.getMessage(), e);
            return R.fail("获取任务列表失败: " + e.getMessage());
        }
    }
}
/**
 * "http://192.168.102.4:9000/satellite/d0926223b6c475c1eb648cf4a6664a94/JL1_250811.tif-1?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=A404%2F20251104%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251104T031139Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=50323c7f56e3461ad8caf76d7c1ce76be3002fc20328732cf7b15c53284ee14d"
 */